﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using UCS.XNA.Utils.Animation;

namespace UCS.XNA.Utils
{
    public class MessageWindow : IDrawable, IFadeAnimatable
    {
        #region Constants

        private const float WINDOW_WIDTH_PERCENTAGE = 0.6f;
        private const float WINDOW_HEIGHT_PERCENTAGE = 0.3f;
        private const string OK_BUTTON_TEXT = "OK";
        private const float OK_BUTTON_TOP_OFFSET_PERCENTATE = 0.7f;
        private const float OK_BUTTON_WIDTH_PERCENTAGE = 0.2f;
        private const float OK_BUTTON_HEIGHT_PERCENTAGE = 0.15f;

        #endregion

        #region Constructors

        public MessageWindow(Rectangle bounds, SpriteBatch spriteBatch, GraphicsDevice graphics, SpriteFont spriteFont)
        {
            this._bounds = bounds;
            this._spriteBatch = spriteBatch;
            this._graphics = graphics;
            this._spriteFont = spriteFont;
            this._message = string.Empty;
            this._wrappedMessage = string.Empty;

            this.InitializeWindowBounds();
            this.InitializeWindowTexture();
            this.InitializeBackgroundTexture();
            this.InitializeBorder();
            this.InitializeMessagePosition();
            this.InitializeOkButton();
        }

        #endregion

        #region Attributes and Properties

        private Rectangle _bounds;
        public Rectangle Bounds
        {
            get { return this._bounds; }
            set
            {
                this._bounds = value;

                this.InitializeWindowBounds();
                this.InitializeBorder();
                this.InitializeOkButton();
            }
        }

        private string _message;
        public string Message
        {
            get { return this._message; }
            set
            {
                this._message = value.Replace("ç", "c").Replace("ã", "a").Replace("ô", "o").Replace("á", "a").Replace("é", "e").Replace("ó", "o").Replace("í", "i");

                this.WrapMessage();
                this.InitializeMessagePosition();
            }
        }

        private SpriteFont _spriteFont;
        public SpriteFont SpriteFont
        {
            get { return this._spriteFont; }
            set { this._spriteFont = value; }
        }

        public int DrawOrder
        {
            get { throw new NotImplementedException(); }
        }

        private bool _visible;
        public bool Visible
        {
            get { return this._visible; }
        }

        private bool _enabled;
        public bool Enabled
        {
            get { return _enabled; }
        }

        private Border _buttonBorder;
        public Border ButtonBorder
        {
            get { return this._buttonBorder; }
            set
            {
                this._buttonBorder = value;

                this._okButton.Border = this._buttonBorder;
            }
        }

        private float _transparency;
        public float Transparency
        {
            get { return this._transparency; }
            set { this._transparency = value; }
        }

        #endregion

        #region Private Members

        private Rectangle _windowBounds;
        private Texture2D _windowTexture;
        private Texture2D _backgroundTexture;
        private SpriteBatch _spriteBatch;
        private GraphicsDevice _graphics;
        private Border _border;
        private Vector2 _messagePosition;
        private string _wrappedMessage;
        private MenuItem _okButton;
        private FadeAnimation _fadeAnimation;

        #endregion

        #region Events

        public event EventHandler<EventArgs> DrawOrderChanged;
        public event EventHandler<EventArgs> VisibleChanged;
        public event EventHandler<EventArgs> Closed;

        #endregion

        #region Private Methods

        private void InitializeWindowBounds()
        {
            int width = (int)(this._bounds.Width * WINDOW_WIDTH_PERCENTAGE);
            int height = (int)(this._bounds.Height * WINDOW_HEIGHT_PERCENTAGE);
            this._windowBounds.X = (int)(this._bounds.X + this._bounds.Width / 2 - width / 2);
            this._windowBounds.Y = (int)(this._bounds.Y + this._bounds.Height / 2 - height / 2);
            this._windowBounds.Width = width;
            this._windowBounds.Height = height;
        }

        private void InitializeWindowTexture()
        {
            this._windowTexture = new Texture2D(this._graphics, 1, 1);
            this._windowTexture.SetData(new Color[] { Color.White });
        }

        private void InitializeBackgroundTexture()
        {
            this._backgroundTexture = new Texture2D(this._graphics, 1, 1);
            this._backgroundTexture.SetData(new Color[] { Color.White });
        }

        private void InitializeBorder()
        {
            Texture2D texture = new Texture2D(this._graphics, 1, 1);
            texture.SetData(new Color[] { Color.Black });

            this._border = new Border(texture, texture);
            this._border.Bounds = this._windowBounds;
        }

        private void InitializeMessagePosition()
        {
            var size = this._spriteFont.MeasureString(this._wrappedMessage);
            int x = (int)(this._windowBounds.X + this._windowBounds.Width / 2 - size.X / 2);
            int y = (int)(this._windowBounds.Y + (this._windowBounds.Height * OK_BUTTON_TOP_OFFSET_PERCENTATE) / 2 - size.Y / 2);
            this._messagePosition = new Vector2(x, y);
        }

        private void InitializeOkButton()
        {
            this._okButton = new MenuItem(OK_BUTTON_TEXT, this._spriteFont);
            Rectangle rect = new Rectangle();
            rect.Width = Convert.ToInt32(this._windowBounds.Width * OK_BUTTON_WIDTH_PERCENTAGE);
            rect.Height = Convert.ToInt32(this._windowBounds.Height * OK_BUTTON_HEIGHT_PERCENTAGE);
            rect.X = this._windowBounds.X + this._windowBounds.Width / 2 - rect.Width / 2;
            rect.Y = Convert.ToInt32(this._windowBounds.Y + this._windowBounds.Height * OK_BUTTON_TOP_OFFSET_PERCENTATE); 
            this._okButton.Bounds = rect;
            this._okButton.Border = this._buttonBorder;
            this._okButton.Click += (sender, e) => this.Hide();
            this._okButton.Show();
        }

        private void WrapMessage()
        {
            this._wrappedMessage = string.Empty;
            string line = string.Empty;
            string[] wordArray = this._message.Split(' ');

            foreach (string word in wordArray)
            {
                if (this._spriteFont.MeasureString(line + word).Length() > this._windowBounds.Width)
                {
                    this._wrappedMessage += line + '\n';
                    line = string.Empty;
                }

                line = line + word + ' ';
            }

            this._wrappedMessage += line;
        }

        private void DrawWindow()
        {
            this._spriteBatch.Draw(this._windowTexture, this._windowBounds, new Color(53, 54, 56) * this._transparency);
        }

        private void DrawBackground()
        {
            this._spriteBatch.Draw(this._backgroundTexture, this._bounds, new Color(0, 0, 0, 0.7f) * this._transparency);
        }

        private void DrawMessage()
        {
            this._spriteBatch.DrawString(this._spriteFont, this._wrappedMessage, this._messagePosition, Color.LightGray * this._transparency);
        }

        private void RaiseClosedEvent()
        {
            if (this.Closed != null)
                this.Closed(this, new EventArgs());
        }

        #endregion

        #region Public Methods

        public void Update(GameTime gameTime, MouseState mouseState, KeyboardState keyboardState)
        {
            if (this._enabled)
            {
                this._okButton.Update(gameTime, mouseState, keyboardState);

                if (this._fadeAnimation != null)
                    this._fadeAnimation.Update(gameTime);

                if (this._border != null)
                    this._border.Update(gameTime);
            }
        }

        public void Draw(GameTime gameTime)
        {
            if (this._visible)
            {
                this._spriteBatch.Begin();

                this.DrawBackground();
                this.DrawWindow();
                this.DrawMessage();

                this._spriteBatch.End();

                this._border.Draw(gameTime);
                this._okButton.Draw(gameTime);
            }
        }

        public void Hide()
        {
            this._fadeAnimation = new FadeOutAnimation(this, 0.1f);
            this._fadeAnimation.AnimationFinished += (sender, e) => { this._visible = false; this._enabled = false; this.RaiseClosedEvent(); };
            this._okButton.Hide();

            if (this._border != null)
                this._border.Hide();
        }

        public void Show()
        {
            this._visible = true;
            this._enabled = true;

            this._fadeAnimation = new FadeInAnimation(this, 0.1f);
            
            if (this._border != null)
                this._border.Show();

            this._okButton.Show();
        }

        #endregion
    }
}
